package org.menacheri.zombie;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandler;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.socket.DatagramChannel;
import org.jboss.netty.channel.socket.DatagramChannelFactory;
import org.jboss.netty.channel.socket.nio.NioDatagramChannelFactory;
import org.menacheri.jetserver.event.Events;
import org.menacheri.jetserver.handlers.netty.EventDecoder;
import org.menacheri.jetserver.util.NettyUtils;
import org.menacheri.zombie.domain.IAM;


public class UDPClient
{
	private static final EventDecoder EVENT_DECODER = new EventDecoder();
	private final ConnectionlessBootstrap b;
	private final IAM iam;
	private final SocketAddress serverAddress;
	
	public UDPClient(ChannelHandler businessHandler, IAM iam,String remoteHost, int remotePort,ExecutorService executorService)
	{
		this.iam = iam;
		this.serverAddress = new InetSocketAddress(remoteHost,remotePort);
		DatagramChannelFactory f = new NioDatagramChannelFactory(executorService);
		// Create only one bootstrap per instance. But use it to make multiple udp channels.
		b = new ConnectionlessBootstrap(f);
		ChannelPipeline p = b.getPipeline();
		p.addLast("eventDecoder",EVENT_DECODER);
		p.addLast("businessHandler", businessHandler);
		b.setOption("broadcast", "true");
	}
	
	public ConnectionlessBootstrap getConnectionlessBootstrap()
	{
		return b;
	}
	
	/**
	 * Method used to create a datagram channel from the bootstrap instance.
	 * @return
	 * @throws UnknownHostException
	 */
	public DatagramChannel createDatagramChannel()  throws UnknownHostException
	{
		String localHost = InetAddress.getLocalHost().getHostAddress();
		DatagramChannel c = (DatagramChannel) b.bind( new InetSocketAddress(localHost,0));
		return c;
	}
	
	public InetSocketAddress getLocalAddress(DatagramChannel c)
	{
		InetSocketAddress add = (InetSocketAddress)c.getLocalAddress();
		return add;
	}
	
	public void start(DatagramChannel c)
	{
		try{
			if(c.isBound())
			{
				// Write the connect statement. TODO repeat till we get start.
				System.out.println("Events.CONNECT: " + Events.CONNECT);
				ChannelBuffer buf = NettyUtils.createBufferForOpcode(Events.CONNECT);
				
				ChannelFuture future = c.write(buf, serverAddress);
				future.addListener(new ChannelFutureListener()
				{
					@Override
					public void operationComplete(ChannelFuture future) throws Exception
					{
						if(!future.isSuccess())
						{
							System.out.println("CONNECT_UDP write to server unsuccessful: " + future.getCause().getMessage());
						}
					}
				});
				
				future.awaitUninterruptibly();
				WriteByte write = new WriteByte(c, serverAddress,iam);
				ZombieClient.SERVICE.scheduleAtFixedRate(write,10000l,5000l, TimeUnit.MILLISECONDS);
			}
			else
			{
				System.out.println("Error: datagram channel is not bound");
			}
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}
